home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
090
/
cmln0586.arc
/
CONTOUR.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1986-04-06
|
24KB
|
937 lines
CONTOUR.PAS
Taken from J. C. Johnston's article in the
May 1986 issue of Computer Language magazine
program Contour;
{ Copyright (C) 1983 by J.C. Johnston
This program displays contour plots of 2D-NMR data on a
digital plotter. The plotter is determined through choice
of plotter include file.
It was written to allow contour plotting of the data and to
make use of the smaller linewidth and higher resolution of
the digital plotter over the Tektronix 4006-1/Printronix
system.
}
const
BoxSideOffset = 3800; {right side of contour box}
BoxTopOffset = 2400; {top of contour box}
DateLength = 11; {length of date string}
DateX = 3000;è DateY = 20;
InitialDisplayIndex = 1;
InitX = 150; {initial x position}
InitY = 200; {initial y position}
LengthBetweenXTicks = 380;
LengthBetweenYTicks = 240;
MaxNumberOfPoints = 2048;
PointsPerDisplayArray = 129;
TitleXStart = 500;
TitleYStart = 20;
XFlipPoint = 2001;
XXCharOffset = 50;
XYCharOffset = 50;
YXCharOffset = 150;
YYCharOffset = -10;
type
DisplayArrayType = ARRAY[1..PointsPerDisplayArray] of INTEGER;
DisplayFileType = FILE of DisplayArrayType;
FileNameType = string[20];
AlphaDirection = (Horizontal, Vertical, HorizInv, VertInv);
TickyType = (ON,OFF);
CharSizeType = (Small, Medium, Large);
var
DisplayFile : DisplayFileType;
DisplayFileName : FileNameType;
Date : String[12];
TextString : String[20];
VExpand : BOOLEAN;
Offset,
LastX, {after Ready sets these variables NO ONE}
LastY, {except MoveTo should change them.}
CurrentX, {these, you can change}
CurrentY,
NumberOfDisplayArrays, {length of long axis in points}
NumberOfDisplayPoints, {length of short axis in points}
MaxZ, {greatest Z axis point}
XEnd, {end of X axis in real units (Hz., etc.)}
XStart, {beginning of X axis in real units}
YEnd, {end of Y axis in real units}
YStart : INTEGER; {beginning of Y axis in real units}
BEEP,è CR,
ESCAPE,
Chars,
Choice : CHAR;
Tick : TickyType;
CharSize : CharSizeType;
procedure ReadData;
{ Reads important data from element 0 of data array}
var
TempArray : DisplayArrayType;
begin
seek(DisplayFile, 0);
read(DisplayFile, TempArray);
NumberOfDisplayArrays := TempArray[1];
NumberOfDisplayPoints := TempArray[2];
MaxZ := TempArray[3];
XEnd := TempArray[4];
XStart := TempArray[5];
YEnd := TempArray[6];
YStart := TempArray[7];
end; {ReadData}
{$I PLOTTER.PAS}
procedure GetDate;
{ It gets and plots the date and title}
begin
TextString := '25-Feb-1986';
MoveTo(DateX, DateY);
PrintText(Medium, Horizontal);
Title;
end; {GetDate}
procedure Contour;
const
MaxEdgesToTraverse = 7;
ContourXOffset = 150;
ContourYOffset = 200;
ContourXScale = 3800.00;
ContourYScale = 2400.00;
type
MapType = ARRAY[0..15, 1..8] of INTEGER;
ContourValueType = ARRAY[1..15] of INTEGER;è
PointArrayType = ARRAY[1..5] of INTEGER;
TraverseArrayType = ARRAY[1..2 , 1..MaxEdgesToTraverse] of INTEGER;
var
I,
TerminalCount,
DisplayCount,
FileIndexOffset,
CellXPosition,
CellYPosition,
RightCellEdge,
RightCellIndex,
FinalDisplayIndex,
ContourCount,
ContourNumber,
StartEdge,
EndEdge,
FileIndex,
MapIndex : INTEGER;
XScale,
YScale,
VExFactor : REAL;
Point : PointArrayType;
ContourValue : ContourValueType;
Traverse : TraverseArrayType;
Map : MapType;
ContourArrayA,
ContourArrayB : DisplayArrayType;
procedure MoveBToA;
{ Moves contour array b to contour array A. }
var
I : INTEGER;
begin
for I := 1 to NumberOfDisplayPoints do
ContourArrayA[I] := ContourArrayB[I];
end; {MoveBToA}
procedure VerticalExpand;
var
I : INTEGER;
RTemp : REAL;è
begin
for I := 1 to NumberOfDisplayPoints do
begin
RTemp := ContourArrayB[I] * VExFactor;
if RTemp > MaxZ
then RTemp := MaxZ;
ContourArrayB[I] := Round(RTemp);
end;
end; {VerticalExpand}
procedure InitMap;
{ Initialize the map }
var
I,J : INTEGER;
begin
for I := 0 to 15 do { Clear the Map }
for J := 1 to 8 do
Map[I,J] := 0;
Map [1,1] := 1; Map [1,2] := 5; Map [1,3] := 6; Map [1,4] := 7;
Map [1,5] := 4;
Map [2,1] := 1; Map [2,2] := 8; Map [2,3] := 7; Map [2,4] := 6;
Map [2,5] := 2;
Map [3,1] := 2; Map [3,2] := 6; Map [3,3] := 7; Map [3,4] := 4;
Map [4,1] := 2; Map [4,2] := 5; Map [4,3] := 8; Map [4,4] := 7;
Map [4,5] := 3;
Map [5,1] := 1; Map [5,2] := 5; Map [5,3] := 2; Map [5,4] := 9;
Map [5,5] := 3; Map [5,6] := 7; Map [5,7] := 4;
Map [6,1] := 1; Map [6,2] := 8; Map [6,3] := 7; Map [6,4] := 3;
Map [7,1] := 3; Map [7,2] := 7; Map [7,3] := 4;
Map [8,1] := 3; Map [8,2] := 6; Map [8,3] := 5; Map [8,4] := 8;
Map [8,5] := 4;
Map [9,1] := 1; Map [9,2] := 5; Map [9,3] := 6; Map [9,4] := 3;
Map [10,1] := 1; Map [10,2] := 8; Map [10,3] := 4; Map [10,4] := 9;
Map [10,5] := 2; Map [10,6] := 6; Map [10,7] := 3;
Map [11,1] := 2; Map [11,2] := 6; Map [11,3] := 3;
Map [12,1] := 2; Map [12,2] := 5; Map [12,3] := 8; Map [12,4] := 4;
Map [13,1] := 1; Map [13,2] := 5; Map [13,3] := 2;è
Map [14,1] := 1; Map [14,2] := 8; Map [14,3] := 4;
end; { InitMap }
procedure FindClusterType(ContourLevel : INTEGER);
{ Figure out which type of cluster this is. }
type
PowerArrayType = ARRAY[1..4] of INTEGER;
var
I : INTEGER;
Sum : REAL;
PowerArray : PowerArrayType;
begin
Sum := 0.00;
for I := 1 to 4 do
begin
if Point[I] >= ContourLevel
then PowerArray[I] := 1
else PowerArray[I] := 0;
Sum := Sum + Point[I];
end;
Point[5] := round(Sum / 4.0);
if Point[5] < ContourLevel
then
begin
for I := 1 to 4 do
if PowerArray[I] = 1
then PowerArray[I] := 0
else PowerArray[I] := 1;
end;
{ This is the Cluster type number. }
MapIndex := PowerArray[1] + (PowerArray[2] * 2)
+ (PowerArray[3] * 4) + (PowerArray[4] * 8);
end; {FindClusterType}
procedure GetTravArray;
{ Generate the Traversal array(s) from the map. A couple of
cluster types use two Traversal arrays. These are marked in
the map by a 9 separating the two traversal paths.
A map entry of 0 means that there is nowhere else to go. }
var
Edge,è J,K,L : INTEGER;
begin
J := 2; { Clear the Traverse Array. }
for K := 1 to MaxEdgesToTraverse do
Traverse[J,K] := 0;
J := 1;
for K := 1 to MaxEdgesToTraverse do
Traverse[J,K] := 0;
K := 1;
L := 1;
repeat
Edge := Map[MapIndex,L];
if Edge = 9
then { Handle the two path cells. }
begin
Traverse[J,K] := 0;
J := J + 1;
K := 1;
end
else { A normal cell or a two path not at sep. }
begin
Traverse[J,K] := Edge;
K := K + 1;
end;
L := L + 1;
until (Edge = 0);
end; {GetTravArray}
procedure DrawCell( BaseX, BaseY, ContourLevel : INTEGER);
{ This procedure draws a cell. }
var
I,J :INTEGER;
FirstPoint : BOOLEAN;
function Interpolate( PointA, PointB : INTEGER): REAL;
{ This function performs the edge interpolation. }
var
Wide,
ConWidth : REAL;
begin
if PointB > PointA
then
beginè Wide := PointB - PointA;
ConWidth := PointB - ContourLevel;
Interpolate := 1.0 - (ConWidth / Wide);
end
else
begin
Wide := PointA - PointB;
ConWidth := PointA - ContourLevel;
Interpolate := ConWidth / Wide;
end;
end; {Interpolate}
procedure FindXY( Edge :INTEGER; VAR X, Y :REAL);
{ Finds the actual X and Y coordinates where the contour
crosses the edge. X and Y are relative to point 1
which is the lower right hand corner. }
begin
case Edge of
1 : begin
X := 0.0;
Y := Interpolate(Point[1], Point[2]);
end;
2 : begin
X := Interpolate(Point[2], Point[3]);
Y := 1.0;
end;
3 : begin
X := 1.0;
Y := Interpolate(Point[4], Point[3]);
end;
4 : begin
X := Interpolate(Point[1], Point[4]);
Y := 0.0;
end;
5 : begin
X := Interpolate( Point[2], Point[5])/2.0;
Y := 1.0 - X;
end;
6 : begin
X := 1.0 - (Interpolate( Point[3], Point[5]) /2.0);
Y := X;
end;
7 : begin
Y := Interpolate( Point[4], Point[5]) / 2.0;
X := 1.0 - Y;
end;
è 8 : begin
Y := Interpolate( Point[1], Point[5]) / 2.0;
X := Y;
end;
end; {case}
end; { FindXY }
procedure DrawContourLine;
{ It draws the lines. }
var
X,Y : REAL;
begin
while Traverse[I,J] <> 0 do
begin
FindXY( Traverse[I,J], X, Y);
CurrentX := BaseX - round( X / XScale);
CurrentY := BaseY - round( Y / YScale);
if FirstPoint
then MoveTo( CurrentX, CurrentY)
else DrawTo( CurrentX, CurrentY);
J := J + 1;
FirstPoint := FALSE;
end;
end; {DrawContourLine}
begin {DrawCell}
MoveTo( BaseX, BaseY);
FirstPoint := TRUE;
I := 1;
J := 1;
DrawContourLine;
if Traverse[2,1] <> 0
then
begin
I := 2;
J := 1;
FirstPoint := TRUE;
DrawContourLine;
end;
FirstPoint := TRUE; {set up to move to next point, not draw}
end; {DrawCell}
procedure DrawContourAxes;
{ It draws the Contour Axes. }
begin
Tick := ON;
Offset := 0;
DrawXAxis( Offset, Tick);è DrawYAxis( Offset, Tick);
Tick := OFF;
Offset := BoxTopOffset;
DrawXAxis( Offset, Tick);
Offset := BoxSideOffset;
DrawYAxis( Offset, Tick);
MoveTo( InitX, InitY);
end; {DrawContourAxes}
procedure VertScale;
{ This procedure allows the user to select the amount of vertical
scale expansion needed.
}
var
GoodChoice :BOOLEAN;
Choice :CHAR;
begin
repeat
GoodChoice := TRUE;
VExpand := TRUE;
writeln;
writeln( ' Please select a vertical scale expansion factor:');
writeln;
writeln(' A 1X Expansion (no change)');
writeln(' B 5X Expansion');
writeln(' C 10X Expansion');
writeln;
write(' Select A,B,or C: ');
readln(Choice);
Choice := UpCase(Choice);
case Choice of
'A' : VExpand := FALSE;
'B' : VExFactor := 5.00;
'C' : VExFactor := 10.00;
else GoodChoice := False;
end;
until GoodChoice;
end; {VertScale}
PROCEDURE ContourParams(VAR ContourCount : INTEGER;
VAR ContourValue : ContourValueType);
VAR
AcceptContours,
GoodContourCount : BOOLEAN;
DeltaPercent,
PercentLevel,
TopContourPercent : REAL;
YesNo : CHAR;
èBEGIN
REPEAT
GoodContourCount := FALSE;
AcceptContours := FALSE;
repeat
writeln;
write(' How many contour levels do you want to use? (3-15) ');
readln(ContourCount);
if (ContourCount >= 3) AND (ContourCount < 16)
then GoodContourCount := TRUE;
until GoodContourCount;
repeat
writeln;
write(' What top contour percent do you want to use?( > 0, <= 100) ');
readln(TopContourPercent);
until ((TopContourPercent > 0.00) AND (TopContourPercent <= 100.00));
TopContourPercent := TopContourPercent/ 100.00;
writeln; writeln(' Contours at: ');
PercentLevel := 1.00;
for I := 1 to ContourCount do { Calculate Contour Levels. }
begin
ContourValue[I] := round(MaxZ * TopContourPercent * PercentLevel);
case I of
1 : DeltaPercent := 0.10;
9 : DeltaPercent := 0.05;
11 : DeltaPercent := 0.02;
end; {case}
PercentLevel := PercentLevel - DeltaPercent;
writeln(' ', ContourValue[I],' ',
((ContourValue[I]/MaxZ)*100.0):5:2,' % of total');
end;
writeln; write(' Do you want to use these values? (Y/N): ');
readln(YesNo);
YesNo := UpCase(YesNo);
if YesNo = 'Y'
then AcceptContours := TRUE;
until AcceptContours;
END; {ContourParams}
begin {Contour}
ClrScr;
FinalDisplayIndex := NumberOfDisplayArrays + 1;
TerminalCount := NumberOfDisplayArrays;
writeln; writeln(' ***** Contour Plotting Routine *****');
VertScale;
ContourParams(ContourCount, ContourValue);
Ready;
DrawContourAxes;
GetDate;
InitMap;
writeln(' Map initialization complete');
XScale := (NumberOfDisplayArrays - 1) / ContourXScale;è
{ Read the first display array. }
FileIndexOffset := 1;
FileIndex := 1;
StartEdge := 1;
EndEdge := NumberOfDisplayPoints - 1;
YScale := NumberOfDisplayPoints / ContourYScale;
seek(DisplayFile, FileIndex); {read the first display array}
read(DisplayFile, ContourArrayB);
FileIndex := FileIndex + FileIndexOffset;
if VExpand
then VerticalExpand;
MoveBToA;
DisplayCount := 1;
repeat
seek(DisplayFile, FileIndex); {read the next display array}
read(DisplayFile, ContourArrayB);
if VExpand
then VerticalExpand;
CellXPosition := round(DisplayCount / XScale) + ContourXOffset;
{ Extract the four corners of the current cell from the arrays}
for RightCellEdge := StartEdge to EndEdge do
begin
Point[3] := ContourArrayA[RightCellEdge];
Point[4] := ContourArrayA[RightCellEdge + 1];
Point[2] := ContourArrayB[RightCellEdge];
Point[1] := ContourArrayB[RightCellEdge + 1];
RightCellIndex := RightCellEdge - Startedge + 1;
CellYPosition := round( RightCellIndex / YScale) + ContourYOffset;
for ContourNumber := 1 to ContourCount do
begin
FindClusterType( ContourValue[ContourNumber]);
if ((MapIndex > 0 ) AND (MapIndex < 15))
then
begin
GetTravArray;
DrawCell(CellXPosition, CellYPosition, ContourValue[ContourNumber]);
end;
end;
end;
FileIndex := FileIndex + FileIndexOffset;
DisplayCount := DisplayCount + 1;
MoveBToA;
write(chr(13),' Processing number: ',(FileIndex - 1));
until DisplayCount = TerminalCount;
MoveTo(0, 0);è writeln;
end; {Contour}
begin {Contou}
BEEP := chr(7); { Define ASCII BEL }
CR := chr(13); { Define ASCII CR }
ESCAPE := chr(27); { Define ASCII ESC }
write('Enter the name of the display file: ');
readln(DisplayFileName);
assign(DisplayFile, DisplayFileName);
reset(DisplayFile);
ReadData;
Contour;
writeln(' End of plotting program.');
end.
PROGRAM MkCon2;
{Make a test Contour Data set.}
const
NumberOfDisplayArrays = 513;
NumberOfDisplayPoints = 129;
XEnd = 5;
XStart = 0;
YEnd = 3;
YStart = 0;
type
DisplayArrayType = ARRAY[1..NumberOfDisplayPoints] of INTEGER;
DisplayFileType = FILE of DisplayArrayType;
FileNameType = string[20];
var
DTemp,
I, J,
MaxZ : INTEGER;
STemp,
LTemp : REAL;
DisplayArray : DisplayArrayType;
DisplayFile : DisplayFileType;
FileName : FileNameType;
procedure WriteData;
{ Writes important data to element 0 of data array}
var
TempArray : DisplayArrayType;è
begin
TempArray[1] := NumberOfDisplayArrays;
TempArray[2] := NumberOfDisplayPoints;
TempArray[3] := MaxZ;
TempArray[4] := XEnd;
TempArray[5] := XStart;
TempArray[6] := YEnd;
TempArray[7] := YStart;
seek(DisplayFile, 0);
write(DisplayFile, TempArray);
end; {WriteData}
begin {main}
MaxZ := 0;
Writeln('Data generator for test of contour plotting software');
write('Enter name of file to be created: ');
readln(FileName);
assign(DisplayFile, FileName);
rewrite(DisplayFile);
WriteData;
for I := 1 to NumberOfDisplayArrays do
begin
writeln(I);
LTemp := I / NumberOfDisplayArrays;
for J := 1 to NumberOfDisplayPoints do
begin
IF (J < 65)
THEN STemp := J / 65;
IF (J = 65)
THEN STemp := 1.0;
IF (J > 90)
THEN STemp := (65 - (J - 65)) / 65;
DTemp := round(1000 * STemp * LTemp);
IF DTemp > MaxZ
then MaxZ := Dtemp;
DisplayArray[J] := Dtemp;
end;
seek(DisplayFile, I);
write(DisplayFile, DisplayArray);
end;
WriteData;
close(DisplayFile);
writeln('Contour data file generation complete');
end.
{AMDEK AMPLOT-II Plotter drivers}
procedure MoveTo( XTo, YTo :INTEGER);è
{ Moves the pen from: LastX, LastY
to: XTo, YTo }
begin
write(AUX,'M', XTo, ',', YTo, CR); {FOR AMDEK AMPLOT-II}
LastX := Xto;
LastY := Yto;
end; {MoveTo}
procedure DrawTo (XTo, YTo :INTEGER);
{ Drops the pen and draws a line from: LastX, LastY
to: XTo, YTo }
begin
write(AUX,'D', XTo, ',', YTo, CR); {FOR AMDEK AMPLOT-II}
LastX := XTo;
LastY := YTo;
end; {DrawTo}
procedure Ready;
{ It Prompts the user to get the plotter ready and hit return. }
var
Dummy : CHAR; {a real dummy variable}
begin
write(' Please get the plotter ready to go and hit return to start.');
write(BEEP, BEEP, BEEP, BEEP, BEEP);
readln;
read(Aux,Dummy);
write(AUX,'Z', CR, 'H', CR, 'J1', CR); {FOR AMDEK AMPLOT-II}
LastX := 0;
LastY := 0;
end; {Ready}
PROCEDURE PrintText(CharSize : CharSizeType; AlphaRotate : AlphaDirection);
{Prints text on the plotter}
var
I, StringLength : INTEGER;
begin
case CharSize of
Small : write(AUX, 'S30,18', CR); {FOR AMDEK AMPLOT-II}
Medium : write(AUX, 'S45,27', CR); {FOR AMDEK AMPLOT-II}
Large : write(AUX, 'S60,36', CR); {FOR AMDEK AMPLOT-II}
end; {case}
StringLength := Length(TextString);
write(AUX, 'P'); {FOR AMDEK AMPLOT-II}è for I := 1 to StringLength do
write(AUX, TextString[I]);
write(AUX, CR); {FOR AMDEK AMPLOT-II}
end; {PrintText}
PROCEDURE Title;
{ Prints a title on the plot so you can identify it. }
begin
MoveTo(TitleXStart, TitleYStart);
TextString := DisplayFileName;
PrintText(Medium, Horizontal);
end; {Title}
procedure DrawXAxis( Offset :INTEGER; Tick :TickyType);
{ Draw an X axis with or without ticks and labels.
Definition of arguements:
Offset - an INTEGER which defines, in plotter units,
an offset for the X axis from Y = InitY
Tick - A structured type which, when set to "ON"
causes ticks to be drawn on the axis.
}
var
XPos, YPos, I,
DeltaXLabel,
XVal, AxisEnd : INTEGER;
DXL, FXVal : REAL;
TenX : BOOLEAN;
begin {DrawXAxis}
YPos := InitY + Offset;
MoveTo(InitX, YPos);
IF Tick = ON
then
begin
write(AUX, 'X0,', LengthBetweenXTicks, ',10', CR);
XPos := InitX - XXCharOffset;
YPos := YPos - XYCharOffset;
MoveTo(XPos, YPos);
DXL := (XEnd - XStart) / 10.0;
DeltaXLabel := trunc(DXL);
FXVal := XStart;
XVal := XStart;
IF DeltaXLabel < 1
THEN
BEGIN
TenX := TRUE;
Str(FXVal:3:1, TextString);
END
ELSE
BEGINè TenX := FALSE;
Str(XVal, TextString);
END;
PrintText(Small, Horizontal);
FOR I := 1 to 10 do
begin
XPos := XPos + LengthBetweenXTicks;
MoveTo(XPos, YPos);
IF TenX
THEN
BEGIN
FXVal := FXVal + DXL;
Str(FXVal:3:1, TextString);
END
ELSE
BEGIN
XVal := XVal + DeltaXLabel;
Str(XVal, TextString);
END;
PrintText(Small, Horizontal);
end;
end
else
begin
AxisEnd := InitX + (LengthBetweenXTicks * 10);
DrawTo(AxisEnd, YPos);
end;
end; {DrawXAxis}
procedure DrawYAxis( Offset :INTEGER; Tick :TickyType);
{ Draws a ticked or unticked unskewed Y axis.
Definition of arguements:
Offset - an INTEGER which defines, in plotter units,
an offset for the Y axis from X = InitX
Tick - A structured type which, when set to "ON"
causes ticks to be drawn on the axis.
}
var
XPos, YPos, I,
DeltaYLabel,
YVal, AxisEnd : INTEGER;
DYL, FYVal : REAL;
TenX : BOOLEAN;
begin {DrawYAxis}
XPos := InitX + Offset;
MoveTo(XPos, InitY);
IF Tick = ON
thenè begin
write(AUX, 'X1,', LengthBetweenYTicks, ',10', CR);
XPos := XPos - YXCharOffset;
YPos := InitY - YYCharOffset;
MoveTo(XPos, YPos);
DYL := (YEnd - YStart) / 10.0;
DeltaYLabel := trunc(DYL);
FYVal := YStart;
YVal := YStart;
IF DeltaYLabel < 1
THEN
BEGIN
TenX := TRUE;
Str(FYVal:3:1, TextString);
END
ELSE
BEGIN
TenX := FALSE;
Str(YVal, TextString);
END;
PrintText(Small,Vertical);
FOR I := 1 to 10 do
begin
YPos := YPos + LengthBetweenYTicks;
MoveTo(XPos, YPos);
IF TenX
THEN
BEGIN
FYVal := FYVal + DYL;
Str(FYVal:3:1, TextString);
END
ELSE
BEGIN
YVal := YVal + DeltaYLabel;
Str(YVal, TextString);
END;
PrintText(Small,Vertical);
end;
end
else
begin
AxisEnd := InitY + (LengthBetweenYTicks * 10);
DrawTo(XPos, AxisEnd);
end;
end; {DrawYAxis}